package server;

import client.PairKey;
import java.io.IOException;
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.security.PublicKey;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.SecretKey;

public class Manager extends UnicastRemoteObject implements ManagerInf {

    protected Game[] games = new Game[10];
    private int numGames = 0;
    public PublicKey publicKey;

    public Manager() throws RemoteException {
        super();
        PairKey s = new PairKey();
        publicKey = s.getpublicKey();
        newGame("Zero");
    }

    public void AssignPublicKey(PublicKey p, int player, int game) {
        games[game].getPlayers()[player].p = p;
    }

    public PublicKey getServerPublicKey() {
        return publicKey;
    }
    public boolean isGameFinished(int game)
    {
        games[game].update();
        return games[game].finished;
    }
    public byte[] genSharedKey(int player, int game) {
        try {

            GenrateSharedKey sharedKey2 = new GenrateSharedKey();
            SecretKey sharedKey = sharedKey2.getAesKey();
            games[game].getPlayers()[player].sharedKey = sharedKey;
//            System.out.println(games[game].getPlayers()[player].sharedKey);
//            System.out.println(sharedKey);
//            System.out.println((SecretKey) Client.bytesToObject(objectToBytes(sharedKey)));
            return PairKey.encrypt(objectToBytes(sharedKey), games[game].getPlayers()[player].p);
        } catch (IOException ex) {
            Logger.getLogger(Manager.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public static byte[] objectToBytes(Object object) throws IOException {
        java.io.ObjectOutputStream out;
        java.io.ByteArrayOutputStream bs;
        bs = new java.io.ByteArrayOutputStream();
        out = new java.io.ObjectOutputStream(bs);
        out.writeObject(object);
        out.close();
        return bs.toByteArray();
    }


    public int newGame(String gameName) throws RemoteException {
        for (int i = 0; i < games.length; i++) {
            if (games[i] == null) {
                games[i] = new Game(gameName);
                numGames++;
                return i;
            }
        }
        return -1;
    }

    public int joinAGame(String player, int gamenum) {
        return games[gamenum].join(new Player(player, new Map()));
    }

    public void leaveAGame(int player, int gamenum) {
        games[gamenum].leave(player);
    }


    public String getPlayerName(int player, int game) {
        return games[game].getPlayers()[player].getName();
    }

    public void setfinishConstruction (boolean b, int player,int game)
    {
        games[game].getPlayers()[player].finishCostruction = b;
    }
    public boolean getfinishConstruction (int player,int game)
    {
        return games[game].getPlayers()[player].finishCostruction;
    }
    public String[] getPlayersName(int game) {

        String[] players = new String[games[game].numberOfPlyers];
        for (int i = 0; i < games[game].numberOfPlyers; i++) {
            players[i] = games[game].getPlayers()[i].getName() + " " + games[game].getPlayers()[i].score + " " + (games[game].getPlayers()[i].lose ? "Lose" : "Not Yet");
        }
        return players;
    }

    public String[] getGamesName() {
        String[] games = new String[numGames];
        for (int i = 0; i < games.length; i++) {
            if (this.games[i] != null) {
                games[i] = i + " " + this.games[i].getName();
            }
        }
        return games;
    }

    public int getPlayerScore(int player, int game) {
        return games[game].getPlayers()[player].score;
    }

    public int getPlayersNumber(int game) {
        return games[game].numberOfPlyers;
    }

    public boolean constructMap(int[] points, int player, int game) {
        Map m = games[game].getPlayers()[player].getMap();
        for (int i = 0; i < points.length; i += 2) {
            if (m.array[points[i]][points[i + 1]] == Map.Status.HAVE) {
                return false;
            }
        }

        for (int i = 0; i < points.length; i += 2) {
            m.array[points[i]][points[i + 1]] = Map.Status.HAVE;
        }
        return true;
    }

    public void hitMap(byte[] bytes,int attacker,int game) {
        byte[] dec = PairKey.decrypt(bytes, games[game].getPlayers()[attacker].p);
        char [][] chars = new char[4][dec.length];
        int w =0 ;
        for (byte b : dec)
        {
            chars[w][0] = (char) b;
            w++;
        }
        int i = chars[0][0]-48, j = chars[1][0]-48, target = chars[2][0]-48;
        Map.Status[][] x = games[game].getPlayers()[target].getMap().array;
        if (x[i][j] == Map.Status.HAVE) {
            x[i][j] = Map.Status.HIT;
            games[game].getPlayers()[attacker].score += 10;
            for (int k = 0; k < 10; k++) {
                for (int c = 0; c < 10; c++) {
                    if (x[k][c].getValue() == 1) // means he have a point of ship not hitted yet
                    {
                        return;
                    }
                }
            }
            games[game].getPlayers()[target].lose = true;
            games[game].update();
        } else if (x[i][j] == Map.Status.EMPTY) {
            x[i][j] = Map.Status.MISS;
            games[game].getPlayers()[attacker].score -= 2;
        }
    }

    public boolean isPlayerLose(int player, int game) {
        return games[game].getPlayers()[player].lose;
    }

    public Ship[] getGameShips(int gamenum) {
        return games[gamenum].getShips();
    }

    public Map getAMap(int player, int game) {
        return games[game].getPlayers()[player].getMap();
    }

    public int[][] getMapArray(int player, int game) {
        return games[game].getPlayers()[player].getMap().getArray();
    }

    public static void main(String[] args) {
        try {
            System.setSecurityManager(new RMISecurityManager());
            Manager m = new Manager();
            Naming.rebind("Server", m);
            System.out.println("System Waiting...");
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (java.net.MalformedURLException e) {
            e.printStackTrace();
        }
    }
}
